package com.upay.android.util.rest;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;

import com.upay.android.domain.request.RequestEnvelope;
import com.upay.android.domain.request.RequestHeader;
import com.upay.android.domain.response.ResponseEnvelope;
import com.upay.android.util.exception.RestException;
import com.upay.android.util.exception.RestExceptionType;


public class RestTemplate<H extends RequestHeader, T extends Object, X extends Object> {

	/* ---- Constants ---- */

	/* ---- Instance Variables ---- */
	
	/* ---- Constructors ---- */
	
	/* ---- Business Methods ---- */
	
	public ResponseEnvelope<X> postForEntity(
		String url, 
		RequestEnvelope<H,T> requestEnvelope, 
		TypeReference<ResponseEnvelope<X>> typeReference,
		List<NameValuePair> headerParameters) {
		
		try {
			InputStream inputStream = this.sendPost(url, parseToJSON(requestEnvelope), headerParameters);		
			return this.parseFromJSON(inputStream, typeReference);
		} catch (JsonMappingException e) {
			throw new RestException(RestExceptionType.ParseException, "JSON Parsing Exception");
		} catch (JsonParseException e) {
			throw new RestException(RestExceptionType.ParseException, "JSON Parsing Exception");
		} catch (IOException e) {
			throw new RestException(RestExceptionType.ConnectionException, "IO Exception");
		} catch (IllegalArgumentException e) {
			throw new RestException(RestExceptionType.IllegalArgumentException, "Illegal URL arguments");
		}
	}
	
	private String parseToJSON(RequestEnvelope<H,T> requestEnvelope) throws JsonGenerationException, JsonMappingException, IOException {
        JsonFactory factory = new JsonFactory();
		ObjectMapper mapper = new ObjectMapper(factory);
		return mapper.writeValueAsString(requestEnvelope);
	}
	
	private ResponseEnvelope<X> parseFromJSON(InputStream inputStream, TypeReference<ResponseEnvelope<X>> typeReference)
	    throws JsonMappingException, JsonParseException, IOException {
        JsonFactory factory = new JsonFactory();
		ObjectMapper mapper = new ObjectMapper(factory);
		ResponseEnvelope<X> responseEnvelope = mapper.readValue(inputStream, typeReference);
		inputStream.close();
		return responseEnvelope;
	}
	
	private InputStream sendPost(String url, String requestEnvelope, List<NameValuePair> headerParameters) throws IOException {
		DefaultHttpClient client = new DefaultHttpClient();
		HttpPost request = new HttpPost(url);
		request.setHeader("Accept", "application/json");
		request.setHeader("Content-type", "application/json");
		applyHeaderParameters(request, headerParameters);
		request.setEntity(new StringEntity(requestEnvelope, "UTF-8"));
		return commit(client, request);
	}

	private InputStream commit(DefaultHttpClient client, HttpRequestBase request)
			throws IOException {
		InputStream inputStream = null;
		try {
			HttpResponse getResponse = client.execute(request);
			inputStream = getResponse.getEntity().getContent();
		} catch (IOException e) {
			client.getConnectionManager().shutdown();
			throw new IOException(e.getMessage());
		}
		return inputStream;
	}
	
	private void applyHeaderParameters(HttpPost request, List<NameValuePair> headerParameters) {
		if (headerParameters != null) {
			for (NameValuePair parameter : headerParameters) {
				request.addHeader(parameter.getName(), parameter.getValue());
			}
		}
	}
	
	
	/* ---- Getters and Setters ---- */
	
}
